//
//  PPNotificationKVObserver.m
//  PPMainLive
//
//  Created by edy on 2022/6/14.
//  Copyright © 2022 twksky. All rights reserved.
//

#import "PPNotificationKVObserver.h"
#import <objc/runtime.h>
@interface PPNotification : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, weak) id obj;
@property (nonatomic, copy) void(^notificationBlock)(NSNotification *notification);
@end
@implementation PPNotification

@end

@interface PPNotificationKVObserver ()

@property (nonatomic, weak) id obj;
@property (nonatomic, copy) PPNotificationKVObserverChangeBlock action;
@property (nonatomic, strong) NSArray *keyPaths;

@property (nonatomic, strong) NSMutableDictionary *names;
@end

@implementation PPNotificationKVObserver

+ (nonnull instancetype)observerForObject:(nonnull id)obj keyPaths:(nonnull NSArray <NSString *>*)keyPaths options:(NSKeyValueObservingOptions)options changeAction:(nonnull PPNotificationKVObserverChangeBlock)action {
    if (!obj) {
        return nil;
    }
    if (!keyPaths || keyPaths.count == 0) {
        return nil;
    }
    if (!action) {
        return nil;
    }
    PPNotificationKVObserver *o = [[PPNotificationKVObserver alloc] init];
    o.obj = obj;
    o.keyPaths = keyPaths;
    for (NSString *keyPath in keyPaths) {
        [obj addObserver:o forKeyPath:keyPath options:options context:NULL];
    }
    o.action = action;
    return o;
}

+ (nonnull instancetype)observerForObject:(nonnull id)obj keyPath:(nonnull NSString *)keyPath options:(NSKeyValueObservingOptions)options changeAction:(nonnull PPNotificationKVObserverChangeBlock)action {
    return [self observerForObject:obj keyPaths:@[keyPath] options:options changeAction:action];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter]removeObserver:self];
    if (!_keyPaths || _keyPaths.count == 0) {
        return;
    }
    for (NSString *keyPath in _keyPaths) {
        [_obj removeObserver:self forKeyPath:keyPath context:NULL];
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (self.action) {
        self.action(object, change, keyPath);
    }
}

- (void)pp_n_notification:(NSNotification *)notification {
    PPNotification *noti = self.names[notification.name];
    if (noti.notificationBlock) {
        noti.notificationBlock(notification);
    }
}

- (void)pp_n_addObserverWithName:(nullable NSNotificationName)aName object:(nullable id)anObject block:(void(^_Nullable)(NSNotification * _Nonnull notification))block {
    if (!aName) {
        return;
    }
    if (!block) {
        return;
    }
    PPNotification *noti = [[PPNotification alloc]init];
    noti.name = aName;
    noti.obj = anObject;
    noti.notificationBlock = block;
    if (self.names[aName]) {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:aName object:anObject];
    }
    [self.names setObject:noti forKey:aName];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pp_n_notification:) name:aName object:anObject];
}

- (void)pp_n_removeObserverName:(nullable NSNotificationName)aName object:(nullable id)anObject {
    if (self.names[aName]) {
        [self.names removeObjectForKey:aName];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:aName object:anObject];
    }
}

- (NSMutableDictionary *)names {
    if (!_names) {
        _names = [NSMutableDictionary dictionary];
    }
    return _names;
}
@end

@interface NSObject()
@property (nonatomic,strong) PPNotificationKVObserver *server;

@property (nonatomic,strong) PPNotificationKVObserver *noti_server;
@end

@implementation NSObject (PPNotificationKVObserver)
- (PPNotificationKVObserver *)server {
    return objc_getAssociatedObject(self, _cmd);
}
- (void)setServer:(PPNotificationKVObserver *)server {
    objc_setAssociatedObject(self, @selector(server), server, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (PPNotificationKVObserver *)noti_server {
    return objc_getAssociatedObject(self, _cmd);
}
- (void)setNoti_server:(PPNotificationKVObserver *)noti_server {
    objc_setAssociatedObject(self, @selector(noti_server), noti_server, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void)pp_addObserverForKeyPath:(nonnull NSString *)keyPath options:(NSKeyValueObservingOptions)options changeAction:(nonnull PPNotificationKVObserverChangeBlock)action {
    if (!self.server) {
        self.server = [PPNotificationKVObserver observerForObject:self keyPath:keyPath options:options changeAction:action];
    }
}

- (void)pp_addObserverForKeyPaths:(nonnull NSArray <NSString *>*)keyPaths options:(NSKeyValueObservingOptions)options changeAction:(nonnull PPNotificationKVObserverChangeBlock)action {
    if (!self.server) {
        self.server = [PPNotificationKVObserver observerForObject:self keyPaths:keyPaths options:options changeAction:action];
    }
}

- (void)pp_addObserverWithName:(nullable NSNotificationName)aName block:(void(^_Nullable)(NSNotification * _Nonnull notification))block {
    if (!self.noti_server) {
        PPNotificationKVObserver *o = [[PPNotificationKVObserver alloc] init];
        [self setNoti_server:o];
    }
    [self.noti_server pp_n_addObserverWithName:aName object:nil block:block];
}
- (void)pp_postNotificationName:(nullable NSNotificationName)aName userInfo:(nullable NSDictionary *)aUserInfo {
    [self pp_postNotificationName:aName object:nil userInfo:aUserInfo];
}
- (void)pp_postNotificationName:(nullable NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo {
    [[NSNotificationCenter defaultCenter] postNotificationName:aName object:anObject userInfo:aUserInfo];
}
- (void)pp_removeObserverName:(nullable NSNotificationName)aName {
    if (!self.noti_server) {
        PPNotificationKVObserver *o = [[PPNotificationKVObserver alloc] init];
        [self setNoti_server:o];
    }
    [self.noti_server pp_n_removeObserverName:aName object:nil];
}
@end
